datatypes w = "http://whattf.org/datatype-draft"

# #####################################################################
##  RELAX NG Schema for HTML 5: Web Forms 1.0 markup                  #
# #####################################################################

## Shared attributes for form controls

common-form.attrs =
(	common-form.attrs.name?
&	common-form.attrs.disabled?
)

common-form.attrs.name =
attribute name {
form.data.nonemptystring
}

common-form.attrs.disabled =
attribute disabled {
w:string "disabled" | w:string ""
}

shared-form.attrs.readonly =
attribute readonly {
w:string "readonly" | w:string ""
}

shared-form.attrs.maxlength =
attribute maxlength {
common.data.integer.non-negative
}

shared-form.attrs.size =
attribute size {
common.data.integer.positive
}

# REVISIT tabindex goes in common.attrs

## Shared attributes for <input>

input.attrs.checked =
attribute checked {
w:string "checked" | w:string ""
}

## Text Field: <input type='text'>

input.text.elem =
element input { input.text.attrs }
input.text.attrs =
(	common.attrs
&	common-form.attrs
&	input.text.attrs.type?
&	shared-form.attrs.maxlength?
&	shared-form.attrs.readonly?
&	shared-form.attrs.size?
&	input.text.attrs.value?
&	(	common.attrs.aria.implicit.textbox
|	common.attrs.aria.role.textbox
|	common.attrs.aria.role.combobox
|	common.attrs.aria.role.searchbox
|	common.attrs.aria.role.spinbutton
)?
)
input.text.attrs.type =
attribute type {
w:string "text"
}
input.text.attrs.value =
attribute value {
form.data.stringwithoutlinebreaks
}

input.elem = input.text.elem

## Password Field: <input type='password'>

input.password.elem =
element input { input.password.attrs }
input.password.attrs =
(	common.attrs
&	common-form.attrs
&	input.password.attrs.type
&	shared-form.attrs.maxlength?
&	shared-form.attrs.readonly?
&	shared-form.attrs.size?
&	input.password.attrs.value?
)
input.password.attrs.type =
attribute type {
w:string "password"
}
input.password.attrs.value =
attribute value {
form.data.stringwithoutlinebreaks
}

input.elem |= input.password.elem

## Checkbox: <input type='checkbox'>

input.checkbox.elem =
element input { input.checkbox.attrs }
input.checkbox.attrs =
(	common.attrs
&	common-form.attrs
&	input.checkbox.attrs.type
&	input.attrs.checked?
&	input.checkbox.attrs.value?
&	(	common.attrs.aria.implicit.checkbox
|	common.attrs.aria.role.checkbox
|	common.attrs.aria.role.button
|	common.attrs.aria.role.menuitemcheckbox
|	common.attrs.aria.role.option
|	common.attrs.aria.role.switch
)?
)
input.checkbox.attrs.type =
attribute type {
w:string "checkbox"
}
input.checkbox.attrs.value =
attribute value {
string #REVISIT require non-empty value?
}

input.elem |= input.checkbox.elem

## Radiobutton: <input type='radio'>

input.radio.elem =
element input { input.radio.attrs }
input.radio.attrs =
(	common.attrs
&	common-form.attrs
&	input.radio.attrs.type
&	input.attrs.checked?
&	input.radio.attrs.value?
&	(	common.attrs.aria.implicit.radio
|	common.attrs.aria.role.radio
|	common.attrs.aria.role.menuitemradio
)?
)
input.radio.attrs.type =
attribute type {
w:string "radio"
}
input.radio.attrs.value =
attribute value {
string #REVISIT require non-empty value?
}

input.elem |= input.radio.elem

## Scripting Hook Button: <input type='button'>

input.button.elem =
element input { input.button.attrs }
input.button.attrs =
(	common.attrs
&	common-form.attrs
&	input.button.attrs.type
&	input.button.attrs.value?
&	(	common.attrs.aria.implicit.button
|	common.attrs.aria.role.button
|	common.attrs.aria.role.link
|	common.attrs.aria.role.menuitem
|	common.attrs.aria.role.menuitemcheckbox
|	common.attrs.aria.role.menuitemradio
|	common.attrs.aria.role.option
|	common.attrs.aria.role.radio
|	common.attrs.aria.role.switch
)?
)
input.button.attrs.type =
attribute type {
w:string "button"
}
input.button.attrs.value =
attribute value {
string #REVISIT require non-empty value?
}

input.elem |= input.button.elem
#REVISIT should this be enabled by a scripting module only?

## Submit Button: <input type='submit'>

input.submit.elem =
element input { input.submit.attrs }
input.submit.attrs =
(	common.attrs
&	common-form.attrs
&	input.submit.attrs.type
&	input.submit.attrs.value?
&	(	common.attrs.aria.implicit.button
|	common.attrs.aria.role.button
)?
)
input.submit.attrs.type =
attribute type {
w:string "submit"
}
input.submit.attrs.value =
attribute value {
string #REVISIT require non-empty value?
}

input.elem |= input.submit.elem

## Reset Button: <input type='reset'>

input.reset.elem =
element input { input.reset.attrs }
input.reset.attrs =
(	common.attrs
&	common-form.attrs
&	input.reset.attrs.type
&	input.reset.attrs.value?
&	(	common.attrs.aria.implicit.button
|	common.attrs.aria.role.button
)?
)
input.reset.attrs.type =
attribute type {
w:string "reset"
}
input.reset.attrs.value =
attribute value {
string #REVISIT require non-empty value?
}

input.elem |= input.reset.elem
# REVISIT does reset make sense outside a form?

## File Upload: <input type='file'>

input.file.elem =
element input { input.file.attrs }
input.file.attrs =
(	common.attrs
&	common-form.attrs
&	input.file.attrs.type
&	input.file.attrs.accept?
&	(	common.attrs.aria?
|	aria.prop.required?
)
)
input.file.attrs.type =
attribute type {
w:string "file"
}
input.file.attrs.accept =
attribute accept {
form.data.mimetypelist
}

input.elem |= input.file.elem

## Hidden String: <input type='hidden'>

input.hidden.elem =
element input { input.hidden.attrs }
input.hidden.attrs =
(	common.attrs
&	common-form.attrs
&	input.hidden.attrs.type
&	input.hidden.attrs.value?
)
input.hidden.attrs.type =
attribute type {
w:string "hidden"
}
input.hidden.attrs.value =
attribute value {
string
}

input.elem |= input.hidden.elem

## Image Submit Button: <input type='image'>

input.image.elem =
element input { input.image.attrs }
input.image.attrs =
(	common.attrs
&	common-form.attrs
&	input.image.attrs.type
&	input.image.attrs.alt
&	input.image.attrs.src?
&	(	common.attrs.aria.implicit.button
|	common.attrs.aria.role.button
|	common.attrs.aria.role.link
|	common.attrs.aria.role.menuitem
|	common.attrs.aria.role.menuitemcheckbox
|	common.attrs.aria.role.menuitemradio
|	common.attrs.aria.role.radio
|	common.attrs.aria.role.switch
)?
)
input.image.attrs.type =
attribute type {
w:string "image"
}
input.image.attrs.alt =
attribute alt {
form.data.nonemptystring
}
input.image.attrs.src =
attribute src {
common.data.uri.non-empty
}

input.elem |= input.image.elem

common.elem.phrasing |= input.elem

## Text Area: <textarea>

	textarea.elem =
		element textarea { textarea.inner & textarea.attrs }
	textarea.attrs =
		(	common.attrs
		&	common-form.attrs
		&	shared-form.attrs.readonly?
		&	textarea.attrs.rows-and-cols-wf1
		&	(	common.attrs.aria.implicit.textbox
			|	common.attrs.aria.role.textbox
			)?
		#FIXME onfocus, onblur, onselect,onchange
		)
		# This is ugly.
		textarea.attrs.rows-and-cols-wf1 =
			textarea.attrs.rows-and-cols-wf1.inner
		textarea.attrs.rows-and-cols-wf1.inner =
			(	textarea.attrs.cols
			&	textarea.attrs.rows
			)
		textarea.attrs.cols =
			attribute cols {
				common.data.integer.positive
			}
		textarea.attrs.rows =
			attribute rows {
				common.data.integer.positive
			}
	textarea.inner =
		( text )

	common.elem.phrasing |= textarea.elem

# Due to limitations with interleave, handling single/multiple selection
# enforcement in RELAX NG seems to be possible but really awkward.
# Tried it. Leaving it to Schematron.

## Select menu option: <option selected>

	option.elem =
		element option { option.inner & option.attrs }
	option.attrs =
		(	common.attrs
		&	common-form.attrs.disabled?
		&	option.attrs.selected?
		&	option.attrs.label?
		&	option.attrs.value?
		&	(	common.attrs.aria.implicit.option
			|	common.attrs.aria.role.option
			)?
		)
		option.attrs.selected =
			attribute selected {
				w:string "selected" | w:string ""
			}
		option.attrs.label =
			attribute label {
				form.data.nonemptystring
			}
		option.attrs.value =
			attribute value {
				string
			}
	option.inner =
		( text )

## Option Group: <optgroup>

	optgroup.elem =
		element optgroup { optgroup.inner & optgroup.attrs }
	optgroup.attrs =
		(	common.attrs
		&	optgroup.attrs.label
		&	common-form.attrs.disabled?
		&	(	common.attrs.aria.role.presentation
			|	common.attrs.aria.role.menuitem
			)?
		)
		optgroup.attrs.label =
			attribute label {
				string
			}
	optgroup.inner =
		(	option.elem*
		&	common.elem.script-supporting*
		)

## Selection Menu: <select>

	select.elem =
		element select { select.inner & select.attrs }
	select.attrs =
		(	common.attrs
		&	common-form.attrs
		&	select.attrs.size?
		&	select.attrs.multiple?
		# FIXME onfocus, onblur, onchange
		&	(	common.attrs.aria.implicit.listbox
			|	common.attrs.aria.role.listbox # aria-multiselectable depends on "multiple" value; check in assertions
			|	common.attrs.aria.role.menu
			)?
		)
		select.attrs.size =
			attribute size {
				common.data.integer.positive
			}
		select.attrs.multiple =
			attribute multiple {
				w:string "multiple" | w:string ""
			}
	select.inner =
		(	optgroup.elem*
		&	option.elem*
		&	common.elem.script-supporting*
		)

	common.elem.phrasing |= select.elem

## Shared Definitions for Complex Button

	button.attrs.value =
		attribute value {
			string
		}
	button.inner =
		( common.inner.phrasing )

## Complex Submit Button: <button type='submit'>

	button.submit.elem =
		element button { button.inner & button.submit.attrs }
	button.submit.attrs =
		(	common.attrs
		&	common-form.attrs
		&	button.submit.attrs.type?
		&	button.attrs.value?
		&	(	common.attrs.aria.implicit.button
			|	common.attrs.aria.role.button
			|	common.attrs.aria.role.checkbox
			|	common.attrs.aria.role.link
			|	common.attrs.aria.role.menuitem
			|	common.attrs.aria.role.menuitemcheckbox
			|	common.attrs.aria.role.menuitemradio
			|	common.attrs.aria.role.option
			|	common.attrs.aria.role.radio
			|	common.attrs.aria.role.tab
			)?
		)
		button.submit.attrs.type =
			attribute type {
				w:string "submit"
			}

	button.elem = button.submit.elem

## Complex Reset Button: <button type='reset'>

	button.reset.elem =
		element button { button.inner & button.reset.attrs }
	button.reset.attrs =
		(	common.attrs
		&	common-form.attrs
		&	button.reset.attrs.type
		&	button.attrs.value? #REVISIT I guess this still affects the DOM
		&	(	common.attrs.aria.implicit.button
			|	common.attrs.aria.role.button
			|	common.attrs.aria.role.checkbox
			|	common.attrs.aria.role.link
			|	common.attrs.aria.role.menuitem
			|	common.attrs.aria.role.menuitemcheckbox
			|	common.attrs.aria.role.menuitemradio
			|	common.attrs.aria.role.option
			|	common.attrs.aria.role.radio
			|	common.attrs.aria.role.switch
			)?
		)
		button.reset.attrs.type =
			attribute type {
				w:string "reset"
			}

	button.elem |= button.reset.elem

## Complex Push Button: <button type='button'>

	button.button.elem =
		element button { button.inner & button.button.attrs }
	button.button.attrs =
		(	common.attrs
		&	common-form.attrs
		&	button.button.attrs.type
		&	button.attrs.value? #REVISIT I guess this still affects the DOM
		&	(	common.attrs.aria.implicit.button
			|	common.attrs.aria.role.button
			|	common.attrs.aria.role.checkbox
			|	common.attrs.aria.role.link
			|	common.attrs.aria.role.menuitem
			|	common.attrs.aria.role.menuitemcheckbox
			|	common.attrs.aria.role.menuitemradio
			|	common.attrs.aria.role.option
			|	common.attrs.aria.role.radio
			|	common.attrs.aria.role.switch
			|	common.attrs.aria.role.tab
			)?
		)
		button.button.attrs.type =
			attribute type {
				w:string "button"
			}

	button.elem |= button.button.elem

	common.elem.phrasing |= button.elem

## Form: <form>

	form.elem =
		element form { form.inner & form.attrs }
	form.attrs =
		(	common.attrs
		&	form.attrs.action? #REVISIT Should this be required anyway?
		&	form.attrs.method?
		&	form.attrs.enctype?
		&	common-form.attrs.name?
		&	form.attrs.accept-charset?
		&	(	common.attrs.aria.implicit.form
			|	common.attrs.aria.landmark.form
			|	common.attrs.aria.landmark.search
			|	common.attrs.aria.role.presentation
			)?
		)
		form.attrs.action =
			attribute action {
				common.data.uri.non-empty
			}
		form.attrs.method =
			attribute method {
				form.attrs.method.data
			}
			form.attrs.method.data =
				( w:string "get" | w:string "post" )
		form.attrs.enctype =
			attribute enctype {
				form.attrs.enctype.data
			}
			form.attrs.enctype.data =
				(	w:string "application/x-www-form-urlencoded"
				|	w:string "multipart/form-data"
				)
		form.attrs.accept-charset =
			attribute accept-charset {
				string
			}
	form.inner =
		( common.inner.flow )

	common.elem.flow |= form.elem

## Fieldset: <fieldset>

	fieldset.elem =
		element fieldset { fieldset.inner & fieldset.attrs }
	fieldset.attrs =
		(	common.attrs
		&	(	common.attrs.aria.implicit.group
			|	common.attrs.aria.role.group
			|	common.attrs.aria.role.radiogroup
			|	common.attrs.aria.role.presentation
			)?
		)
	fieldset.inner =
		legend.elem?, common.inner.flow

	common.elem.flow |= fieldset.elem

## Label: <label>

	label.elem =
		element label { label.inner & label.attrs }
	label.attrs =
		(	common.attrs
		&	label.attrs.for?
		)
		label.attrs.for =
			attribute for {
				common.data.idref
			}
	label.inner =
		( common.inner.phrasing ) #REVISIT making obvious guess

	common.elem.phrasing |= label.elem

